package com.beaven.imserver.net.providers;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

/**
 * 用于设置Jersey的Json转换器
 * 用于替换JacksonJsonProvider
 * <p>
 * 该工具类完成了，把Http请求中的请求数据转换为Model实体，
 * 同时也实现了把返回的Model实体转换为Json字符串
 * 并输出到Http的返回体中。
 *
 * @param <T> 任意类型范型定义
 */
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

  // 全局Gson
  private static final Gson gson;

  static {
    GsonBuilder gsonBuilder = new GsonBuilder()
        .serializeNulls()
        // 仅仅处理带有@Expose注解的变量
        .excludeFieldsWithoutExposeAnnotation()
        // 支持Map
        .enableComplexMapKeySerialization();
    // 添加对Java8LocalDateTime时间类型的支持
    gsonBuilder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeConverter());
    gson = gsonBuilder.create();
  }

  @Override public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations,
      MediaType mediaType) {
    return true;
  }

  @Override
  public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType,
      MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
      throws IOException, WebApplicationException {
    try (JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream, "UTF-8"))) {
      return gson.fromJson(jsonReader, genericType);
    }
  }

  @Override public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
      MediaType mediaType) {
    return true;
  }

  @Override public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations,
      MediaType mediaType) {
    return -1;
  }

  @Override public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
      MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
      throws IOException, WebApplicationException {
    try (
        JsonWriter jsonWriter = gson.newJsonWriter(new OutputStreamWriter(entityStream, "UTF-8"))) {
      gson.toJson(t, genericType, jsonWriter);
      jsonWriter.close();
    }
  }

  public static Gson getGson() {
    return gson;
  }
}
